﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
using BrainTechLLC;
using BrainTechLLC.FilterExtensions;
using BrainTechLLC.ThreadSafeObjects;
using BrainTechLLC.WPF;
using DirectoryViewModels;
using ImageAppDataModels;
using System.Diagnostics;
using System.IO;

namespace ImageAppViewModels
{
    public partial class FileImageCollectionViewModel : ObservableCollection<FileImageViewModel>, IWatcherNotifiable
    {
        protected Action<string> _pathDoesNotExist;
        protected Action<string> _UpdateAction;
        public bool _stopLazyLoad;
        protected ThreadSafeList<string> _pathsDoNotExist;
        protected Lockable _lock = new Lockable();
        public FileImageViewModelCollection _Displayed;

        /// <summary>
        /// Provides a notification prior to clearing all items in the collection
        /// </summary>
        public event EventHandler ClearingAll;

        /// <summary>
        /// QUick way to find a file image view model by file name
        /// </summary>
        public ThreadSafeLookup<string, FileImageViewModel> QuickLookupByFileName = new ThreadSafeLookup<string, FileImageViewModel>(StringComparer.OrdinalIgnoreCase);

        /// <summary>
        /// Event raised after all thumbnails have been invalidated
        /// </summary>
        protected FastSmartWeakEvent<EventHandler> _thumbnailsInvalidated = new FastSmartWeakEvent<EventHandler>();
        public event EventHandler ThumbnailsInvalidated { add { _thumbnailsInvalidated.Add(value); } remove { _thumbnailsInvalidated.Remove(value); } }

        /// <summary>
        /// Event raised when the collection needs to be refreshed from directories/files
        /// </summary>
        protected FastSmartWeakEvent<EventHandler> _wantRefreshDirectories = new FastSmartWeakEvent<EventHandler>();
        public event EventHandler WantRefreshDirectories { add { _wantRefreshDirectories.Add(value); } remove { _wantRefreshDirectories.Remove(value); } }

        public int _lastVisibleCount = 0;
        protected bool _doNotRaiseCollectionChanged;
        protected Dispatcher _dispatcher;
        protected FileImageCollectionStatsViewModel _stats;
        protected Thread _backgroundWorker;
        public FileImageCollection _fileImageCollection = new FileImageCollection();
        protected DirectoriesToExamineViewModel _directoriesToExamine;
        protected bool _needRefresh;
        protected int _changeGeneration;
        protected object _watcherLockObject = new object();
        protected List<DirectoryWatcher> _watchers;
        public int _currentIndex = -1;

        public bool NeedsRefresh { set { _needRefresh = value; } }

        public Settings CurrentSettings
        {
            get { return Settings.Values; }
        }

        public bool FullViewerVisible
        {
            get { return Settings.Values.FullViewerVisible; }
            set
            {
                if (Settings.Values.FullViewerVisible != value)
                {
                    Settings.Values.FullViewerVisible = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("FullViewerVisible"));
                }
            }
        }

        public bool FilterExplorerVisible
        {
            get { return Settings.Values.FilterExplorerVisible; }
            set
            {
                if (Settings.Values.FilterExplorerVisible != value)
                {
                    Settings.Values.FilterExplorerVisible = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("FilterExplorerVisible"));
                }
            }
        }

        public bool FolderExplorerVisible
        {
            get { return Settings.Values.FolderExplorerVisible; }
            set
            {
                if (Settings.Values.FolderExplorerVisible != value)
                {
                    Settings.Values.FolderExplorerVisible = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("FolderExplorerVisible"));
                }
            }
        }

        public bool DirectoriesVisible
        {
            get { return Settings.Values.DirectoriesVisible; }
            set
            {
                if (Settings.Values.DirectoriesVisible != value)
                {
                    Settings.Values.DirectoriesVisible = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("DirectoriesVisible"));
                }
            }
        }

        public bool ThumbnailsVisible
        {
            get { return Settings.Values.ThumbnailsVisible; }
            set
            {
                if (Settings.Values.ThumbnailsVisible != value)
                {
                    Settings.Values.ThumbnailsVisible = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("ThumbnailsVisible"));
                }
            }
        }

        public DirectoriesToExamineViewModel DirectoriesToExamine
        {
            get { return _directoriesToExamine; }
            set
            {
                if (_directoriesToExamine != value)
                {
                    _directoriesToExamine = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("DirectoriesToExamine"));
                }
            }
        }

        public FileImageCollection ImageCollection
        {
            get { return _fileImageCollection; }
        }

        public int LoadedThumbs
        {
            get { return _fileImageCollection._latestCountInfo == null ? 0 : _fileImageCollection._latestCountInfo.LoadedThumbCount; }
        }

        public int LoadedByteCount
        {
            get { return _fileImageCollection._latestCountInfo == null ? 0 : (int)_fileImageCollection._latestCountInfo.LoadedByteCount; }
        }

        public double ThumbWidth
        {
            get { return Settings.Values.ThumbWidth; }
            set { Settings.Values.ThumbWidth = value; }
        }

        public double ThumbHeight
        {
            get { return Settings.Values.ThumbHeight; }
            set { Settings.Values.ThumbHeight = value; }
        }

        /// <summary>
        /// Returns a count of loaded files
        /// </summary>
        public int LoadedFileCount
        {
            get { return _fileImageCollection._latestCountInfo.LoadedFileCount; }
        }

        /// <summary>
        /// Returns a count of cached image sources
        /// </summary>
        public int CachedImageSources
        {
            get { return _fileImageCollection._latestCountInfo.CachedImageSources; }
        }

        /// <summary>
        /// Returns a count of cached thumbnail image source objects
        /// </summary>
        public int CachedThumbnailImageSources
        {
            get
            {
                int cnt = 0;

                for (int n = 0; n < Count; n++)
                {
                    var vm = this[n];

                    if (vm.ThumbImageSourceCached != null)
                    {
                        cnt++;
                    }
                }

                return cnt;
            }
        }

        /// <summary>
        /// Returns a count of additional info structures loaded
        /// </summary>
        public int LoadedAdditionalInfoCount
        {
            get { return _fileImageCollection._latestCountInfo.LoadedAdditionalInfoCount; }
        }

        /// <summary>
        /// Returns a count of loaded bitmaps
        /// </summary>
        public int LoadedBitmaps
        {
            get { return _fileImageCollection._latestCountInfo.LoadedBitmapCount; }
        }

        public FilterCollection Filters
        {
            get { return _fileImageCollection.Filters; }
            set
            {
                if (_fileImageCollection.Filters != value)
                {
                    // DetachFilterEvents(_fileImageCollection.Filters);
                    _fileImageCollection.Filters = value;
                    // AttachFilterEvents(_fileImageCollection.Filters);
                    SetFilters();
                    OnPropertyChanged(new PropertyChangedEventArgs("Filters"));
                }
            }
        }

        public FileImageCollectionStatsViewModel Stats
        {
            get { return _stats; }
            set
            {
                if (_stats != value)
                {
                    _stats = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("Stats"));
                }
            }
        }

        /// <summary>
        /// Returns a byte count for unused view models
        /// </summary>
        public long UnusedByteCount
        {
            get
            {
                long n;
                GetUnusedViewModels(out n, false, false);
                return n;
            }
        }

        /// <summary>
        /// Returns the number of file images in the collection
        /// </summary>
        public int FileImageCollectionCount
        {
            get { return _fileImageCollection.Count; }
        }

        /// <summary>
        /// Returns the number of file image view models in the collection
        /// </summary>
        public int FileImageViewModelCollectionCount
        {
            get { return this.Count; }
        }

        /// <summary>
        /// Returns the number of visible thumbnails
        /// </summary>
        public int NumberVisible
        {
            get { return _lastVisibleCount; }
        }

        public void CheckCleanup()
        {
            long bytes = UnusedByteCount;
            long bytesUnused;

            if (bytes > Settings.Values.UnusedBytesBeforeCleanup)
            {
                GetUnusedViewModels(out bytesUnused, true, false);
                _stats.Invalidate();
            }
        }

        public void UpdateBlur()
        {            
            foreach (var item in this)
            {
                item.UpdateBlur();
            }
        }

        /// <summary>
        /// Assigns the specified filter collection to all file image view models in the collection
        /// </summary>
        /// <param name="filters"></param>
        protected void SetFilters(FilterCollection filters)
        {
            List<FileImageViewModel> list;
            _lock.AquireLock();
            {
                list = this.ToList();
            }
            _lock.ReleaseLock();

            foreach (var item in list)
            {
                item.Item.Filters = filters;
            }
        }

        protected void SetFilters()
        {
            SetFilters(Filters);
        }

        /// <summary>
        /// Returns a list containing a copy of all items in the collection
        /// </summary>
        /// <returns></returns>
        protected List<FileImageViewModel> GetAll()
        {
            List<FileImageViewModel> results = new List<FileImageViewModel>(this.Items);
            return results;
        }

        /// <summary>
        /// Returns a list of unused view models and/or performs memory cleanup
        /// </summary>
        /// <param name="unusedByteCount"></param>
        /// <param name="immediateCleanup">if true, frees memory of unused view models as per settings & params</param>
        /// <param name="addToList">if true, returns a list of unused view models, otherwise simply executes the routine and returns no list</param>
        /// <returns></returns>
        public List<FileImageViewModel> GetUnusedViewModels(out long unusedByteCount, bool immediateCleanup, bool addToList)
        {
            Dispatcher d = null;

            if (immediateCleanup)
            {
                d = (Application.Current == null) ? null : Application.Current.Dispatcher;
            }

            unusedByteCount = 0;

            List<FileImageViewModel> results = new List<FileImageViewModel>();
            var all = GetAll();

            // since cleanup is done on the dispatcher thread (later), we need to see how many
            // thumbnails are currently in memory, and then decrement the count whenever a
            // thumbnail is scheduled for cleanup
            int thumbsInMemoryNow = ThumbnailProvider.ThumbsInMemory.Count;

            for (int n = 0; n < all.Count; n++)
            {
                var item = all[n];

                // If item is false, the view model may be unused
                if (item.Visible == false)
                {
                    if (item.BitmapLoaded || item.Item.ImageBytesCached != null)
                    {
                        unusedByteCount += item.FileSize;

                        if (immediateCleanup)
                        {
                            BackgroundRelease(d, item);
                        }

                        if (addToList)
                        {
                            results.Add(item);
                        }
                    }
                    else if (item.Item.ThumbnailCached != null && thumbsInMemoryNow > Settings.Values.MaxThumbsInMemory)
                    {
                        unusedByteCount += item.FileSize;

                        if (immediateCleanup)
                        {
                            BackgroundRelease(d, item);
                        }

                        thumbsInMemoryNow--;

                        if (addToList)
                        {
                            results.Add(item);
                        }
                    }
                }
            }

            return results;
        }

        private static void BackgroundRelease(Dispatcher d, FileImageViewModel item)
        {
            d.RunOnDispatcher(new OneParamDelegate((o) =>
            {
                try
                {
                    if (!item.ThumbVisible)
                    {
                        item.Release(ReleaseOptions.All);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }),
            DispatcherPriority.Normal,
            item);
        }

        private void OnUnderlyingCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            _stats.Invalidate();
        }

        /// <summary>
        /// Adds a file image and corresponding view model by file name
        /// </summary>
        /// <param name="s"></param>
        public void AddByFileName(string fullPath)
        {
            FileImage fileImage = _fileImageCollection.CreateNew(fullPath, Filters);
            FileImageViewModel vm = new FileImageViewModel(fileImage, Filters, this);
            AddViewModel(vm);
        }

        /// <summary>
        /// Adds a view model to the collection and the quick lookup
        /// </summary>
        /// <param name="vm"></param>
        public void AddViewModel(FileImageViewModel vm)
        {
            if (QuickLookupByFileName.Add(vm.FileName, vm))
            {
                _lock.AquireLock();
                {
                    Add(vm);
                }
                _lock.ReleaseLock();
            }
        }

        /// <summary>
        /// Removes a view model by file name
        /// </summary>
        /// <param name="s"></param>
        public void RemoveByFileName(string fullPath)
        {
            var found = QuickLookupByFileName[fullPath];

            if (found != null)
            {
                RemoveViewModel(found);
            }
        }

        /// <summary>
        /// Removes the specified view model 
        /// </summary>
        /// <param name="found"></param>
        /// <returns></returns>
        public bool RemoveViewModel(FileImageViewModel found)
        {
            bool ret = QuickLookupByFileName.Remove(found.FileName);

            if (ret)
            {
                this.RemoveAll(true, (vm) => vm.FileName == found.FileName);
            }

            _fileImageCollection.RemoveAll(true, (fi) => fi.FileName == found.FileName);

            return ret;
        }

        public void FileNameChanged(string oldFileName, string newFileName)
        {
            // TODO
        }

        public FileImageCollectionViewModel(FileImageViewModelCollection displayed)
        {
            _Displayed = displayed;
        }

        public FileImageCollectionViewModel(DirectoriesToExamineViewModel directoriesToExamine, FilterCollection filters, bool doNotStartDirReader, Action<string> pathDoesNotExist, ThreadSafeList<string> pathsDoNotExist, FileImageViewModelCollection displayed)
            : this(displayed)
        {
            Filters = filters;
            _pathDoesNotExist = pathDoesNotExist;
            _pathsDoNotExist = pathsDoNotExist;
            _stats = new FileImageCollectionStatsViewModel(this);
            _directoriesToExamine = directoriesToExamine;
            _directoriesToExamine.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_directoriesToExamine_CollectionChanged);
            _fileImageCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_fileImageCollection_CollectionChanged);

            if (!doNotStartDirReader)
            {
                ThreadStart ts = new ThreadStart(LazyLoad);
                _backgroundWorker = ts.CreateAndRunThread(true);                
            }

            _changeGeneration++;
        }

        void _fileImageCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            OnUnderlyingCollectionChanged(e);
        }

        void _directoriesToExamine_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            lock (_watcherLockObject)
            {
                if (_watchers != null)
                {
                    var v = from watch in _watchers.AsParallel()
                            select watch;

                    v.ForAll(watch =>
                    {
                        watch.Detach(this);
                        watch.Dispose();
                    });
                    //foreach (var watch in _watchers)
                    //{
                    //    watch.Detach(this);
                    //    watch.Dispose();
                    //}
                }

                _changeGeneration++;
                _needRefresh = true;
            }
        }

        public void ClearAll()
        {
            if (ClearingAll != null)
            {
                ClearingAll(this, EventArgs.Empty);
            }
            QuickLookupByFileName.Clear();
            ClearItems();
            _needRefresh = true;
        }

        public FileImageCollectionViewModel(DirectoriesToExamineViewModel directoriesToExamine, Dispatcher dispatcher, FilterCollection filters, bool doNotStartDirReader, Action<string> pathDoesNotExist, ThreadSafeList<string> pathsDoNotExist, FileImageViewModelCollection displayed)
            : this(directoriesToExamine, filters, doNotStartDirReader, pathDoesNotExist, pathsDoNotExist, displayed)
        {
            _dispatcher = dispatcher;
        }

        public void AddByPath(string pathName, Action<string> pathDoesNotExist, ThreadSafeList<string> pathsDoNoExist)
        {
            try
            {
                var results = _fileImageCollection.AddByPath(pathName, pathDoesNotExist, pathsDoNoExist);

                if (pathsDoNoExist.Find(pathName) == null)
                {
                    AddWatcher(pathName);
                }

                AddViewModelsFromFileImages(results, _changeGeneration);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public void AddWatchers(params string[] pathNames)
        {
            var paths = from path in pathNames.AsParallel()
                        select path;

            paths.ForAll(pathName => AddWatcher(pathName));

            var morePaths = from path in pathNames.AsParallel()                            
                            select path;

            morePaths.ForAll(pathName => AddWatcher(pathName));
            
            //foreach (var pathName in pathNames)
            //{
            //    AddWatcher(pathName);
            //}
        }

        public void AddWatcher(string pathName)
        {
            try
            {
                if (_watchers == null)
                {
                    Interlocked.CompareExchange<List<DirectoryWatcher>>(ref _watchers, new List<DirectoryWatcher>(), null);
                }

                lock (_watcherLockObject)
                {
                    try
                    {
                        if (Directory.Exists(pathName))
                        {
                            var watch = new DirectoryWatcher(pathName, "*.jpg");
                            _watchers.Add(watch);
                            watch.Attach(this);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public void AddByPathNames(Action<string> pathDoesNotExist, ThreadSafeList<string> noLongerExist, params string[] pathNames)
        {
            try
            {
                var results = _fileImageCollection.AddByPathNames(pathDoesNotExist, noLongerExist, pathNames);

                var paths = pathNames.ToList();
                var noLonger = noLongerExist.AllItems;
                paths.RemoveAll(o => noLonger.IndexOf(o) >= 0);
                AddWatchers(paths.ToArray());

                AddViewModelsFromFileImages(results, _changeGeneration);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public List<Pair<IFileImage, IFileImage>> FindDuplicatesSync(Action<string> update)
        {
            return _fileImageCollection.FindDuplicates((s) => update(s));
        }

        public void FindDuplicates(Action<List<Pair<IFileImage, IFileImage>>> completed, Action<string> update)
        {
            _UpdateAction = update;
            ParameterizedThreadStart ts = new ParameterizedThreadStart(FindDuplicatesWorker);
            Thread t = new Thread(ts);
            t.Name = "FindDuplicatesWorker";
            t.Start(completed);
            t.IsBackground = true;
            t.Priority = ThreadPriority.BelowNormal;
        }

        protected void FindDuplicatesWorker(object o)
        {
            Action<List<Pair<IFileImage, IFileImage>>> completed = o as Action<List<Pair<IFileImage, IFileImage>>>;
            var result = _fileImageCollection.FindDuplicates((s) => _UpdateAction(s));
            completed(result);
        }

        public FileImageViewModel Find(string fileName)
        {
            try
            {
                return QuickLookupByFileName[fileName];
                //for (int n = Count - 1; n >= 0; n--)
                //{
                //    if (this[n].FileName == fileName)
                //        return this[n];
                //}
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            return null;
        }

        protected int FindIndex(string fileName)
        {
            try
            {
                int idx = -1;
                if (!_Displayed.Index.TryGetValue(fileName, out idx))
                {
                    return 0;
                }
                return idx;

                //for (int n = Count - 1; n >= 0; n--)
                //{
                //    if (this[n].FileName == fileName)
                //        return n;
                //}
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            return -1;
        }

        public void InvalidateAllThumbnails(bool reloadAllFiles)
        {
            try
            {
                foreach (var item in this)
                {
                    item.ClearThumbnail();
                }

                if (reloadAllFiles)
                {
                    _directoriesToExamine.Refresh();

                    _wantRefreshDirectories.Raise(this, EventArgs.Empty);
                }

                RaiseThumbsInvalidated();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public void RaiseThumbsInvalidated()
        {
            _thumbnailsInvalidated.Raise(this, EventArgs.Empty);
        }

        public FileImageViewModel GetNext(string fileName, bool adjustIndex)
        {
            int idx = _currentIndex + 1;

            if (_currentIndex < 0)
            {
                idx = FindIndex(fileName);
                idx++;
            }

            if (adjustIndex)
            {
                _currentIndex = idx;
            }

            if (idx >= Count)
                return null;

            // return this[idx];
            return this._Displayed[idx];
        }

        public FileImageViewModel GetPrevious(string fileName, bool adjustIndex)
        {
            int idx = _currentIndex - 1;

            if (_currentIndex < 0)
            {
                idx = FindIndex(fileName);
                idx--;
            }

            if (adjustIndex)
            {
                _currentIndex = idx;
            }

            if (idx < 0)
                return null;

            //return this[idx];
            return this._Displayed[idx];
        }

        public bool Delete(FileImageViewModel viewModel)
        {
            return _fileImageCollection.Delete(viewModel.FileImage);
        }

        public bool Delete(IEnumerable<FileImageViewModel> viewModels)
        {
            bool result = true;

            foreach (var viewModel in viewModels)
            {
                bool b = Delete(viewModel);
                result = result && b;
            }

            return result;
        }

        public FileImageViewModel AddNew(IFileImage fileImage)
        {
            try
            {
                var vm = QuickLookupByFileName[fileImage.FileName];

                if (vm == null)
                {
                    vm = new FileImageViewModel(fileImage as FileImage, Filters, this);
                    AddViewModel(vm);
                }
                return vm;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return null;
            }
        }

        protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (_doNotRaiseCollectionChanged)
                return;

            base.OnCollectionChanged(e);
        }

        protected bool _skipRest = false;
        protected bool _addedOne;
        protected bool _firstBatchDone;

        protected void DoneAdding(List<FileImageViewModel> viewModels)
        {
            try
            {
                List<FileImageViewModel> viewModelsNew = new List<FileImageViewModel>();

                bool startAt48 = false;
                if (!_addedOne && viewModels.Count > 0)
                {
                    startAt48 = true;
                    _addedOne = true;
                    _dispatcher.RunOnDispatcher(new VoidDelegate(() =>
                     {
                         int n = 0;

                         for (; n < viewModels.Count; n++)
                         {
                             var vm = viewModels[n];

                             if (n < 48)
                             {
                                 if (!_skipRest)
                                 {
                                     try
                                     {
                                         if (QuickLookupByFileName.ContainsKey(vm.FileName))
                                         {
                                             OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, vm));
                                         }
                                     }
                                     catch (Exception ex)
                                     {
                                         _skipRest = true;
                                         Debug.WriteLine(ex.ToString());
                                         Console.WriteLine(ex.ToString());
                                     }
                                 }
                             }
                         }

                         if (n == 48)
                         {
                             _skipRest = false;
                         }

                         _firstBatchDone = true;

                     }), DispatcherPriority.Send);
                }

                //while (startAt48 && !_firstBatchDone)
                //while (!_firstBatchDone)
                //{
                //    Thread.Sleep(200);
                //}
                for (int n = startAt48 ? 48 : 0; n < viewModels.Count; n++)
                {
                    var vm = viewModels[n];

                    if (QuickLookupByFileName.ContainsKey(vm.FileName))
                    {
                        viewModelsNew.Add(vm);
                    }
                }

                _dispatcher.RunOnDispatcher(new OneParamDelegate((o) =>
                {
                    if (!_skipRest)
                    {
                        try
                        {
                            var vmodels = o as List<FileImageViewModel>;
                            for (int i = 0; i < vmodels.Count; i++)
                            {
                                var vm = vmodels[i];

                                if (QuickLookupByFileName.ContainsKey(vm.FileName))
                                {
                                    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, vm));
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.ToString());
                        }
                    }
                }),
                DispatcherPriority.ApplicationIdle,
                viewModelsNew);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        protected void AddViewModelsFromFileImages(List<IFileImage> collection, int currentGeneration)
        {
            int added = 0;
            List<FileImageViewModel> viewModels = new List<FileImageViewModel>();

            if (_dispatcher != null)
            {
                for (int n = 0; n < collection.Count; n++)
                {
                    if (currentGeneration != _changeGeneration)
                        break;

                    var image = collection[n];

                    try
                    {
                        _doNotRaiseCollectionChanged = true;
                        var vm = AddNew(image);
                        _doNotRaiseCollectionChanged = false;
                        added++;
                        viewModels.Add(vm);

                        if (added == collection.Count - 1)
                        {
                            DoneAdding(viewModels);
                            Thread.Sleep(5);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                    }
                }
            }
        }

        public void ClearCachedThumbnailImageSources(int downTo)
        {
            int n = 0;
            int countNow = Count;

            try
            {
                for (int i = CachedThumbnailImageSources; i >= downTo; i--)
                {
                    for (; n < countNow; n++)
                    {
                        var vm = this[n];

                        if (vm.ThumbVisible)
                        {
                            if (downTo > 0)
                                downTo--;

                            continue;
                        }

                        if (vm.ThumbImageSourceCached != null)
                        {
                            vm.ThumbImageSourceCached = null;
                            n++;
                            break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public void MarkEverythingDirty(bool filterThumbs)
        {
            for (int i = this.Count - 1; i >= 0; i--)
            {
                this[i].MarkEverythingDirty(filterThumbs);
            }
        }
    }
}


//for (int n = 0; n < collection.Count; n++)
//{
//    if (currentGeneration != _changeGeneration)
//        break;

//    // HEREHERE
//    _dispatcher.RunOnDispatcher(new TwoParamDelegate((o1, o2) =>
//    {
//        if (currentGeneration != _changeGeneration)
//            return;

//        try
//        {
//            IFileImage image = o1 as IFileImage;
//            _doNotRaiseCollectionChanged = true;
//            var vm = AddNew(image);
//            _doNotRaiseCollectionChanged = false;
//            added++;
//            viewModels.Add(vm);

//            if (added == collection.Count - 1)
//            {
//                DoneAdding(viewModels);
//            }
//        }
//        catch (Exception ex)
//        {
//            Console.WriteLine(ex.ToString());
//        }
//    }),                
//    DispatcherPriority.Normal,                        
//    collection[n], n);
//}


// DoneAdding
//for (int n = 0; n < viewModels.Count; n++)
//{
//    var vm = viewModels[n];

//    if (n < 48)
//    {
//        _dispatcher.RunOnDispatcher(new TwoParamDelegate((o, o2) =>
//        {
//            int nValue = (int)o2;
//            if (!_skipRest)
//            {
//                try
//                {
//                    var vmodel = o as FileImageViewModel;

//                    if (QuickLookupByFileName.ContainsKey(vmodel.FileName)) //  && this.Contains(vmodel))
//                    {
//                        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, vmodel));
//                    }
//                }
//                catch (Exception ex)
//                {
//                    _skipRest = true;
//                    Debug.WriteLine(ex.ToString());
//                    Console.WriteLine(ex.ToString());
//                }
//            }

//            if (nValue == 48)
//            {
//                _skipRest = false;
//            }
//        }),
//        DispatcherPriority.ContextIdle,
//        vm, n);
//    }
//    else
//    {
//        if (QuickLookupByFileName.ContainsKey(vm.FileName))
//        {
//            viewModelsNew.Add(vm);
//        }
//    }
//}
